#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "rfs-defs.h"

int recibir(PACKAGE *package, struct sockaddr_in *sock, int flag) {
    int sock_size = sizeof (*sock), n, i;
    // recibo el paquete

    if (flag != 0) {
        errno = EAGAIN;
        n = -1;
        for (i = 0; errno == EAGAIN && i <= TIMEOUT && n < 0; i++) {
            n = recvfrom(package->from, package, sizeof (PACKAGE), flag, (struct sockaddr *) sock, &sock_size);
            usleep(1000);
        }
        if (n < 0 && errno != EAGAIN) {
            perror("Error recibiendo paquete:\n");
        } else
            if (n < 0 && errno == EAGAIN) {
            printf("Timeout recibiendo paquete.\n");
        }
    } else {
        n = recvfrom(package->from, package, sizeof (PACKAGE), flag, (struct sockaddr *) sock, &sock_size);
    }
    return n;
}

int enviar(PACKAGE *package, struct sockaddr_in sock, int mode) {
    int count = 0, i = 0, j = 0;
    if (mode == ACK) {
        do {
            count = sendto(package->from, package, sizeof (PACKAGE), 0, (struct sockaddr *) & sock, sizeof (sock));
            if ((i = recibir(package, &sock, MSG_DONTWAIT)) < 0) {
                perror("Ocurrió un error recibiendo el ACK.\n");
                return ERROR;
            } else
                if (package->op != ACK) {
                printf("El servidor envió '%i' cuando se esperaba ACK.\n", package->op);
                return ERROR;
            }
        } while (i < 0 && j++ < RE_ATTEMPTS);
        if (i < 0)
            return ERROR;
    } else {
        count = sendto(package->from, package, sizeof (PACKAGE), 0, (struct sockaddr *) & sock, sizeof (sock));
    }
    return count;
}

long segmentar(char * data, PACKAGE * package, struct sockaddr_in sock, unsigned long size) {
    long i = 0;
    for (i = 0; (i * DATA_LENGTH) < size; i++) {
        package->offset = ((i + 1) * DATA_LENGTH) < size ? DATA_LENGTH : (size - (i * DATA_LENGTH));
        memcpy(package->data, &data[i * DATA_LENGTH], package->offset);
        enviar(package, sock, ACK);
    }
    return (i-1) * DATA_LENGTH + package->offset;
}

long ensamblar(char ** data, PACKAGE * package, struct sockaddr_in sock) {
    int i = 0;
    do {
        if (recibir(package, NULL, MSG_DONTWAIT) < 0)
            return ERROR;
        *data = (char *) realloc(*data, i * DATA_LENGTH + package->offset);
        memmove(&(*data)[i * DATA_LENGTH], package->data, package->offset);
        package->op = ACK;
        enviar(package, sock, 0);
        i++;
    } while (package->offset == DATA_LENGTH);
    return (i - 1)*DATA_LENGTH + package->offset;
};

int open_socket(struct sockaddr_in dir) {
    int sd;

    if ((sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
        return (-1);

    return sd;
}

int close_socket(int sd) {
    return close(sd);
}

int begin_connection(PACKAGE *package) {
    package->op = INIT;
    char * data_aux = malloc(sizeof (DATA_LENGTH));
    strcpy(data_aux, package->data);

    int n = -1, j = 0;
    do {
        enviar(package, dir, 0);
        if ((n = recibir(package, NULL, MSG_DONTWAIT)) < 0) {
            printf("Falló el intento de conexión nº %i.\n", j + 1);
        } else {
            // Seteo la direccion y trato de conectarme.
            dir.sin_port = htons((int) ntohl(atoi(package->data)));
        }
    } while (n < 0 && j++ < RE_ATTEMPTS);
    if (n < 0)
        return ERROR;
    printf("===========================\n");
    printf("Fue negociada con éxito la conexión con el servidor,\nque otorgó el puerto '%i'.\n", ntohs(dir.sin_port));
    printf("===========================\n");
    strcpy(package->data, data_aux);
    free(data_aux);
    return OK;
}